﻿using log4net;
using Newtonsoft.Json;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.IO.Compression;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Web;
using Winista.Text.HtmlParser;
using Winista.Text.HtmlParser.Filters;
using Winista.Text.HtmlParser.Lex;
using Winista.Text.HtmlParser.Util;

namespace Spiders
{
    class BaiduRobot
    {
        /// <summary>
        /// 存储baidu.com域名下的Cookie
        /// </summary>
        private Dictionary<string, Cookie> BaiduCookie = null;
        /// <summary>
        /// 存储获取到的随机Cookie生成ID
        /// </summary>
        private string Id;
        /// <summary>
        /// 日志对象
        /// </summary>
        private static readonly ILog log = LogManager.GetLogger(typeof(BaiduRobot));
        /// <summary>
        /// 登陆时需要计算一个时间差，该字段用来存储初始时间
        /// </summary>
        private long InitTime;
        /// <summary>
        /// 存储用户信息
        /// </summary>
        private UserInfo UserInfo;
        /// <summary>
        /// 存储关键字问题
        /// </summary>
        private KeywordQuestions KeywordQuestions;
        /// <summary>
        /// 存储用户Stoken
        /// </summary>
        private string Stoken;
        /// <summary>
        /// 提交问题回答时需要一个初始时间，该字段用来存储该时间
        /// </summary>
        private long SubmitInitTime;

        /// <summary>
        /// 构造函数
        /// </summary>
        public BaiduRobot()
        {
            BaiduCookie = new Dictionary<string, Cookie>();
        }

        /// <summary>
        /// 访问百度知道首页
        /// </summary>
        /// <returns></returns>
        public void HttpGetZhidaoMainPage()
        {
            log.Info("首次访问百度知道首页");
            HttpWebResponse response = BaiduRobotHelper.GetZhidaoMainPageResponse();
            // 处理Cookie
            log.Info("记录BAIDUID Cookie");
            if (response.Headers[HttpResponseHeader.SetCookie] != null)
            {
                string cookies = response.Headers[HttpResponseHeader.SetCookie];
                Cookie BAIDUID = BaiduRobotHelper.ZhidaoMainPageCookieHandler(cookies);
                BaiduCookie["BAIDUID"] = BAIDUID;
            }
            else
            {
                log.Error("应该返回BAIDUID Cookie，但目前没有返回");
                throw new Exception();
            }

            // 获取cookie id
            log.Info("获取响应流中附带的随机Id");
            string responseString = HttpWebRequestUtility.ReadResponseStream(response.GetResponseStream());
            Lexer lexer = new Lexer(responseString);
            Parser parser = new Parser(lexer);
            NodeFilter filter = new TagNameFilter("SCRIPT");

            NodeList list = parser.Parse(filter);
            for (int i = 0; i < list.Count; i++)
            {
                var node = list[i];
                if (node.FirstChild == null || !node.FirstChild.GetText().Contains("hm.baidu.com/h.js")) continue;

                string script = node.FirstChild.GetText();
                Id = script.Substring(script.IndexOf("h.js%3F") + 7, script.LastIndexOf("' type=") - script.IndexOf("h.js%3F") - 7);
            }
            log.Info("生成Hm_lvt Cookie和Hm_lpvt Cookie");
            Cookie lvtCookie = BaiduRobotHelper.HmLvtCookieHandler(Id, ".baidu.com", "http://zhidao.baidu.com");
            Cookie lpvtCookie = BaiduRobotHelper.HmLpvtCookieHandler(Id, ".baidu.com", "http://zhidao.baidu.com");
            BaiduCookie.Add("hmlvt", lvtCookie);
            BaiduCookie.Add("hmlpvt", lpvtCookie);
        }

        public void HttpGetNewBaiduIDCookie()
        {
            log.Info("获取新的BAIDUID Cookie");
            CookieCollection coll = new CookieCollection();
            coll.Add(BaiduCookie["BAIDUID"]);
            coll.Add(BaiduCookie["hmlvt"]);
            coll.Add(BaiduCookie["hmlpvt"]);
            HttpWebResponse response = BaiduRobotHelper.GetNewBaiduIDCookie(coll);
            // 处理新的BAIDUID Cookie
            if (response.Headers[HttpResponseHeader.SetCookie] != null)
            {
                string cookie = response.Headers[HttpResponseHeader.SetCookie];
                Cookie BAIDUID = BaiduRobotHelper.ZhidaoMainPageCookieHandler(cookie);
                BaiduCookie["BAIDUID"] = BAIDUID;
            }
        }

        public string HttpGetLoginInfo()
        {
            log.Info("检查是否登陆");
            CookieCollection coll = new CookieCollection();
            coll.Add(BaiduCookie["BAIDUID"]);
            coll.Add(BaiduCookie["hmlvt"]);
            coll.Add(BaiduCookie["hmlpvt"]);
            HttpWebResponse response = BaiduRobotHelper.GetLoginInfoResponse(coll);
            Stream stream = new GZipStream(response.GetResponseStream(), CompressionMode.Decompress);
            string responseString = HttpWebRequestUtility.ReadResponseStream(response.GetResponseStream());
            return responseString;
        }

        public string HttpGetApiInfo()
        {
            // 记录初始时间
            InitTime = DateTime.UtcNow.GetTime();
            log.Info("获取API信息");
            CookieCollection coll = new CookieCollection();
            coll.Add(BaiduCookie["BAIDUID"]);
            HttpWebResponse response = BaiduRobotHelper.GetApiInfo(coll);
            log.Info("处理获取API信息时返回的Cookie");
            if (response.Headers[HttpResponseHeader.SetCookie] != null)
            {
                string cookie = response.Headers[HttpResponseHeader.SetCookie];
                Cookie HOSUPPORT = CookieUtility.CookieHandler(cookie, "HOSUPPORT");
                BaiduCookie["HOSUPPORT"] = HOSUPPORT;
            }
            else
            {
                log.Error("获取API信息后应返回Cookie，目前没有返回");
                throw new Exception();
            }
            string responseString = HttpWebRequestUtility.ReadResponseStream(response.GetResponseStream());
            log.Info("获取token");
            string substring = responseString.Substring(responseString.IndexOf("token"));
            substring = substring.Substring(0, substring.IndexOf(","));
            substring = substring.Substring(substring.IndexOf(':') + 1).Trim();
            string token = substring.Substring(substring.IndexOf('"') + 1, substring.LastIndexOf('"') - 1);
            return token;
        }

        public void HttpLoginCheck(string token, string username)
        {
            log.Info("登陆前检查并获取Cookie");
            CookieCollection coll = new CookieCollection();
            coll.Add(BaiduCookie["BAIDUID"]);
            coll.Add(BaiduCookie["HOSUPPORT"]);
            HttpWebResponse response = BaiduRobotHelper.LoginCheck(token, username, coll);
            log.Info("处理登陆前检查响应头中返回的Cookie");
            if (response.Headers[HttpResponseHeader.SetCookie] != null)
            {
                string cookie = response.Headers[HttpResponseHeader.SetCookie];
                Cookie UBI = new Cookie();
                Cookie PASSID = new Cookie();
                string ubi, passid;
                if (cookie.IndexOf("UBI") > cookie.IndexOf("PASSID"))
                {
                    ubi = cookie.Substring(cookie.IndexOf("UBI"));
                    passid = cookie.Substring(0, cookie.IndexOf("UBI") - 1);
                    
                }
                else
                {
                    ubi = cookie.Substring(0, cookie.IndexOf("PASSID") - 1);
                    passid = cookie.Substring(cookie.IndexOf("PASSID"));
                }
                PASSID = CookieUtility.CookieHandler(passid, "passid");
                UBI = CookieUtility.CookieHandler(ubi, "ubi");
                BaiduCookie["PASSID"] = PASSID;
                BaiduCookie["UBI"] = UBI;
            }
            else
            {
                log.Error("登陆前检查响应头中未包含Cookie字段");
                throw new Exception();
            }
        }

        public void HttpLogin(string token, string username, string password)
        {
            log.Info("登陆百度");
            // 构造Cookie
            CookieCollection coll = new CookieCollection();
            coll.Add(BaiduCookie["BAIDUID"]);
            coll.Add(BaiduCookie["HOSUPPORT"]);
            coll.Add(BaiduCookie["UBI"]);
            // 构造请求体
            string url = System.Web.HttpUtility.UrlEncode("http://zhidao.baidu.com/static/html/v3Jump_bf2a8d6e.html"); 
            string queryString = "&charset=GBK&token=" + token + "&tpl=ik&apiver=v3&tt=" + DateTime.UtcNow.GetTime() +
                "&codestring=&safeflg=0&u=" + System.Web.HttpUtility.UrlEncode("http://zhidao.baidu.com/") + "&isPhone=false&quick_user=0&logintype=dialogLogin&splogin=rate&username=" + username + "&password=" + password +
                "&verifycode=&mem_pass=on&ppui_logintime=" + (DateTime.UtcNow.GetTime() - InitTime) + "&callback=parent.bd__pcbs__" + AnyRadixConvert.H2X((long)(Math.Floor((new Random()).NextDouble() * 2147483648)), 36).ToLower();
            string data = "staticpage=" + url + queryString;
            HttpWebResponse response = response = BaiduRobotHelper.Login(data, coll);
            log.Info("处理登陆后返回的Cookie");
            if (response.Headers[HttpResponseHeader.SetCookie] != null)
            {
                string cookie = response.Headers[HttpResponseHeader.SetCookie];
                ArrayList list = CookieUtility.SetCookieStringHandler(cookie);
                for (int i = 0; i < list.Count; i++)
                {
                    string s = list[i].ToString();
                    string key = s.Substring(0, s.IndexOf('='));
                    try
                    {
                        BaiduCookie.Add(key, CookieUtility.CookieHandler(s, key));
                    }
                    catch
                    {
                        BaiduCookie[key + i.ToString()] = CookieUtility.CookieHandler(s, key);
                    }
                }
            }
            else
            {
                log.Error("登陆后应该返回Cookie，目前没有返回");
                throw new Exception();
            }
        }

        public void HttpRefresh()
        {
            log.Info("刷新百度知道首页，并获取用户Stoken");
            CookieCollection coll = new CookieCollection();
            coll.Add(BaiduCookie["BAIDUID"]);
            coll.Add(BaiduCookie["BDUSS"]);
            coll.Add(BaiduCookie["hmlvt"]);
            coll.Add(BaiduCookie["hmlpvt"]);
            HttpWebResponse response = BaiduRobotHelper.Refresh(coll);
            string s = HttpWebRequestUtility.ReadResponseStream(response.GetResponseStream());
            Lexer lexer = new Lexer(s);
            Parser parser = new Parser(lexer);
            NodeFilter filter = new TagNameFilter("SCRIPT");

            NodeList list = parser.Parse(filter);
            for (int i = 0; i < list.Count; i++)
            {
                var node = list[i];
                if (node.FirstChild == null || !(node.FirstChild.GetText().Contains("F.context('user'") && node.FirstChild.GetText().Contains("stoken"))) continue;

                string script = node.FirstChild.GetText();
                Stoken = script.Substring(script.IndexOf("stoken: '") + 9, 32);
            }
        }

        public void HttpGetUserInfo()
        {
            log.Info("获取并记录登陆用户信息");
            CookieCollection coll = new CookieCollection();
            coll.Add(BaiduCookie["BAIDUID"]);
            coll.Add(BaiduCookie["BDUSS"]);
            coll.Add(BaiduCookie["hmlvt"]);
            coll.Add(BaiduCookie["hmlpvt"]);
            HttpWebResponse response = BaiduRobotHelper.GetUserInfo(coll);
            string s = HttpWebRequestUtility.ReadResponseStream(response.GetResponseStream());
            UserInfo = JsonConvert.DeserializeObject<UserInfo>(s);
            // 构造IK_PASS Cookie
            Cookie IK_PASS = new Cookie();
            IK_PASS.Name = "IK_PASS";
            IK_PASS.Value = HttpUtility.UrlEncode(BaiduCookie["BAIDUID"].Value + UserInfo.UserName);
            long l = 1000L * 60L * 60L * 24L * 30L;
            IK_PASS.Expires = DateTime.UtcNow.AddMilliseconds(l);
            IK_PASS.Domain = "baidu.com";
            IK_PASS.Path = "/";
            BaiduCookie["IK_PASS"] = IK_PASS;
            // 构造IM_old Cookie
            Cookie IM_old = new Cookie();
            IM_old.Name = "IM_old";
            IM_old.Value = "0|" + AnyRadixConvert.H2X(DateTime.UtcNow.GetTime(), 36).ToString().ToLower();
            IM_old.Domain = "baidu.com";
            IM_old.Path = "/";
            IM_old.Expires = DateTime.UtcNow;
            BaiduCookie["IM_old"] = IM_old;
        }

        public void HttpGetAllKeywordQuestion()
        {
            log.Info("获取全部关键字对应的百度知道问题");
            SubmitInitTime = DateTime.Now.GetTime();
            CookieCollection coll = new CookieCollection();
            coll.Add(BaiduCookie["BAIDUID"]);
            coll.Add(BaiduCookie["BDUSS"]);
            coll.Add(BaiduCookie["hmlvt"]);
            coll.Add(BaiduCookie["hmlpvt"]);
            coll.Add(BaiduCookie["IK_PASS"]);
            coll.Add(BaiduCookie["IM_old"]);
            HttpWebResponse response = BaiduRobotHelper.GetAllKeywordQuestions(coll);
            string s = HttpWebRequestUtility.ReadResponseStream(response.GetResponseStream());
            KeywordQuestions = JsonConvert.DeserializeObject<KeywordQuestions>(s);
            // 构造 PMS_JT Cookie
            Cookie PMS_JT = new Cookie();
            PMS_JT.Name = "PMS_JT";
            PMS_JT.Value = HttpUtility.UrlEncode("({\"s\":" + DateTime.Now.GetTime() + ",\"r\":\"http://zhidao.baidu.com\"})");
            PMS_JT.Domain = "baidu.com";
            PMS_JT.Path = "/";
            BaiduCookie["PMS_JT"] = PMS_JT;
        }

        public void HttpPostSubmit()
        {
            log.Info("开始随机提取出某个问题");
            IList<Question> questions = KeywordQuestions.List;
            Question q = DomUtility.FindRandomQuestion(questions);
            log.Info("随机提取出问题，问题描述：" + q.Title + "；Qid：" + q.Qid);
            log.Info("开始构造请求体");
            StringBuilder builder = new StringBuilder();
            builder.Append("cm=100009");
            builder.Append("&qid=" + q.Qid);
            builder.Append("&title=" + (q.IsFromWap ? HttpUtility.UrlEncode(";wap_question_replyed") : string.Empty));
            builder.Append("&answerfr=" + (q.IsFromWap ? HttpUtility.UrlEncode(";wap_question_replyed") : string.Empty));
            builder.Append("&entry=qb_home_keyword");
            builder.Append("&co=" + HttpUtility.UrlEncode("<p>我也想知道，等达人回答！<br/><p>"));
            builder.Append("&cite=&rich=1");
            builder.Append("&utdata=" + HttpUtility.UrlEncode(DomUtility.GenerateUtdata(SubmitInitTime)));
            builder.Append("&stoken=" + Stoken);
            // 构造Cookie
            //Cookie IK_EGG_QB = new Cookie();
            //IK_EGG_QB.Name = "IK_EGG_QB";
            //IK_EGG_QB.Value = "8";
            //IK_EGG_QB.Domain = "baidu.com";
            //IK_EGG_QB.Path = "/";
            //BaiduCookie["IK_EGG_QB"] = IK_EGG_QB;
            Cookie IK_USERVIEW = new Cookie();
            IK_USERVIEW.Name = "IK_USERVIEW";
            IK_USERVIEW.Value = "1";
            IK_USERVIEW.Domain = "baidu.com";
            IK_USERVIEW.Path = "/";
            IK_USERVIEW.Expires = DateTime.Now.AddMilliseconds(28800000);
            BaiduCookie["IK_USERVIEW"] = IK_USERVIEW;
            Cookie IK_PRIVILEGE = new Cookie();
            IK_PRIVILEGE.Name = "IK_PRIVILEGE";
            IK_PRIVILEGE.Value = "1";
            IK_PRIVILEGE.Domain = "baidu.com";
            IK_PRIVILEGE.Path = "/";
            IK_PRIVILEGE.Expires = DateTime.Now.AddMilliseconds(28800000);
            BaiduCookie["IK_PRIVILEGE"] = IK_PRIVILEGE;
            Cookie bdshare_firstime = new Cookie();
            bdshare_firstime.Name = "bdshare_firstime";
            bdshare_firstime.Value = DateTime.Now.GetTime().ToString();
            bdshare_firstime.Domain = "baidu.com";
            bdshare_firstime.Path = "/";
            bdshare_firstime.Expires = DateTime.Now.AddMilliseconds(28800000);
            BaiduCookie["bdshare_firstime"] = bdshare_firstime;
            string BAIDUID = BaiduCookie["BAIDUID"].Value;
            string key = "IK_" + BAIDUID.Split(':')[0];
            Cookie IK_BAIDUID = new Cookie();
            IK_BAIDUID.Name = key;
            IK_BAIDUID.Value = "1";
            IK_BAIDUID.Domain = "baidu.com";
            IK_BAIDUID.Path = "/";
            IK_BAIDUID.Expires = DateTime.Now.AddMilliseconds(28800000);
            BaiduCookie["IK_BAIDUID"] = IK_BAIDUID;
            CookieCollection coll = new CookieCollection();
            coll.Add(BaiduCookie["BAIDUID"]);
            coll.Add(BaiduCookie["BDUSS"]);
            coll.Add(BaiduCookie["hmlvt"]);
            coll.Add(BaiduCookie["hmlpvt"]);
            coll.Add(BaiduCookie["IK_PASS"]);
            coll.Add(BaiduCookie["IM_old"]);
            coll.Add(BaiduCookie["bdshare_firstime"]);
            coll.Add(BaiduCookie["IK_BAIDUID"]);
            coll.Add(BaiduCookie["IK_USERVIEW"]);
            coll.Add(BaiduCookie["IK_PRIVILEGE"]);
            HttpWebResponse response = BaiduRobotHelper.SubmitAnswer(coll, q.Qid, builder.ToString());
            if (response.StatusCode == HttpStatusCode.OK)
            {
                string s = HttpWebRequestUtility.ReadResponseStream(response.GetResponseStream());
                ErrorInfo info = JsonConvert.DeserializeObject<ErrorInfo>(s);
            }
            else
            {

            }
        }        
    }
}